/*
 * ========================================
 * RISC-V 最小操作系统格式化输出实现
 * ========================================
 * 
 * 这个文件实现了内核的printf功能，包括：
 * 1. 数字转换算法（十进制、十六进制）
 * 2. 格式字符串解析
 * 3. 可变参数处理
 * 4. 控制台控制功能
 * 
 * 学习要点：
 * - 理解xv6的printint实现原理
 * - 掌握C语言可变参数的使用
 * - 学习数字转换的算法实现
 * - 了解ANSI转义序列的使用
 */

#include "../include/types.h"
#include "../include/printf.h"
#include <stdarg.h>

// ========================================
// 全局变量和常量
// ========================================

// 数字字符表，用于不同进制的转换
static const char digits[] = "0123456789abcdef";

// ========================================
// 数字转换核心算法
// ========================================
//
// 这个函数实现了数字到字符串的转换，是printf的核心算法
// 参考xv6的printint实现，但进行了简化和优化
//
// 参数说明：
// - num: 要转换的数字
// - base: 进制（10表示十进制，16表示十六进制）
// - sign: 是否处理符号（1表示处理，0表示不处理）
// - buf: 输出缓冲区
// - buf_size: 缓冲区大小
//
// 返回值：转换后的字符串长度
static int print_number(long long num, int base, int sign, char *buf, int buf_size)
{
    int i = 0;
    unsigned long long x;
    
    // ========================================
    // 处理负数
    // ========================================
    //
    // 为什么要将负数转为正数处理？
    // 1. 简化算法：只需要处理正数的转换
    // 2. 避免溢出：直接对负数取模可能导致问题
    // 3. 统一处理：正数和负数使用相同的转换逻辑
    //
    if (sign && num < 0) {
        // 注意：这里不能直接写 x = -num
        // 因为如果num是INT_MIN，-num会溢出
        // 所以我们先转换为无符号数，再取负
        x = (unsigned long long)(-(long long)num);
    } else {
        x = (unsigned long long)num;
    }
    
    // ========================================
    // 数字转换循环
    // ========================================
    //
    // 使用do-while循环而不是while循环的原因：
    // 1. 即使num为0，也要输出一个'0'字符
    // 2. 确保至少执行一次转换
    //
    do {
        // 取模得到当前位的数字
        buf[i++] = digits[x % base];
        
        // 整除得到下一位要处理的数字
        x /= base;
        
        // 防止缓冲区溢出
        if (i >= buf_size - 1) {
            buf[buf_size - 1] = '\0';
            return buf_size - 1;
        }
    } while (x != 0);
    
    // ========================================
    // 添加负号
    // ========================================
    //
    // 如果原数是负数，在数字前面添加负号
    // 注意：负号要加在数字的后面（因为我们要逆序输出）
    //
    if (sign && num < 0) {
        buf[i++] = '-';
    }
    
    // 添加字符串结束符
    buf[i] = '\0';
    
    return i;
}

// ========================================
// 数字输出函数
// ========================================
//
// 将转换后的数字输出到控制台
// 注意：由于我们是从低位到高位转换的，需要逆序输出
//
static void output_number(long long num, int base, int sign)
{
    char buf[32];  // 32位数字最多需要32个字符（包括符号）
    int len;
    
    // 转换数字为字符串
    len = print_number(num, base, sign, buf, sizeof(buf));
    
    // 逆序输出（从高位到低位）
    for (int i = len - 1; i >= 0; i--) {
        console_putc(buf[i]);
    }
}

// ========================================
// 指针输出函数
// ========================================
//
// 输出指针地址（十六进制格式）
// 格式：0x12345678
//
static void output_pointer(uint64 ptr)
{
    // 输出"0x"前缀
    console_putc('0');
    console_putc('x');
    
    // 输出64位地址（16个十六进制数字）
    for (int i = 0; i < 16; i++) {
        // 从高位到低位输出
        int shift = (15 - i) * 4;
        uint64 digit = (ptr >> shift) & 0xF;
        console_putc(digits[digit]);
    }
}

// ========================================
// 格式字符串解析
// ========================================
//
// 这是printf的核心函数，负责解析格式字符串
// 使用状态机的方式处理不同的格式
//
int printf(const char *fmt, ...)
{
    va_list ap;  // 可变参数列表
    int count = 0;  // 输出字符计数
    
    // 初始化可变参数列表
    va_start(ap, fmt);
    
    // 遍历格式字符串
    for (int i = 0; fmt[i] != '\0'; i++) {
        if (fmt[i] != '%') {
            // ========================================
            // 普通字符：直接输出
            // ========================================
            console_putc(fmt[i]);
            count++;
        } else {
            // ========================================
            // 格式字符：需要特殊处理
            // ========================================
            i++;  // 跳过'%'字符
            
            // 检查格式字符
            switch (fmt[i]) {
                case 'd':  // 十进制整数
                    output_number(va_arg(ap, int), 10, 1);
                    count++;
                    break;
                    
                case 'u':  // 无符号十进制整数
                    output_number(va_arg(ap, unsigned int), 10, 0);
                    count++;
                    break;
                    
                case 'x':  // 十六进制整数
                    output_number(va_arg(ap, unsigned int), 16, 0);
                    count++;
                    break;
                    
                case 'p':  // 指针地址
                    output_pointer(va_arg(ap, uint64));
                    count += 18;  // "0x" + 16个十六进制数字
                    break;
                    
                case 'c':  // 单个字符
                    console_putc(va_arg(ap, int));
                    count++;
                    break;
                    
                case 's':  // 字符串
                    {
                        char *s = va_arg(ap, char*);
                        if (s == 0) {
                            // 处理NULL指针
                            console_puts("(null)");
                            count += 6;
                        } else {
                            // 输出字符串
                            for (int j = 0; s[j] != '\0'; j++) {
                                console_putc(s[j]);
                                count++;
                            }
                        }
                    }
                    break;
                    
                case '%':  // 输出'%'字符
                    console_putc('%');
                    count++;
                    break;
                    
                case '\0':  // 格式字符串结束
                    goto end;
                    
                default:  // 未知格式字符
                    // 输出'%'和未知字符，让用户知道有问题
                    console_putc('%');
                    console_putc(fmt[i]);
                    count += 2;
                    break;
            }
        }
    }
    
end:
    // 清理可变参数列表
    va_end(ap);
    
    return count;
}

// ========================================
// sprintf实现
// ========================================
//
// 将格式化输出写入缓冲区而不是控制台
// 实现方式与printf类似，但输出目标不同
//
int sprintf(char *buf, const char *fmt, ...)
{
    va_list ap;
    int count = 0;
    int buf_pos = 0;
    
    va_start(ap, fmt);
    
    for (int i = 0; fmt[i] != '\0'; i++) {
        if (fmt[i] != '%') {
            // 普通字符
            buf[buf_pos++] = fmt[i];
            count++;
        } else {
            i++;  // 跳过'%'
            
            switch (fmt[i]) {
                case 'd': {
                    char num_buf[32];
                    int len = print_number(va_arg(ap, int), 10, 1, num_buf, sizeof(num_buf));
                    // 逆序复制到目标缓冲区
                    for (int j = len - 1; j >= 0; j--) {
                        buf[buf_pos++] = num_buf[j];
                        count++;
                    }
                    break;
                }
                
                case 's': {
                    char *s = va_arg(ap, char*);
                    if (s == 0) {
                        const char *null_str = "(null)";
                        for (int j = 0; null_str[j] != '\0'; j++) {
                            buf[buf_pos++] = null_str[j];
                            count++;
                        }
                    } else {
                        for (int j = 0; s[j] != '\0'; j++) {
                            buf[buf_pos++] = s[j];
                            count++;
                        }
                    }
                    break;
                }
                
                case 'c': {
                    buf[buf_pos++] = va_arg(ap, int);
                    count++;
                    break;
                }
                
                case '%': {
                    buf[buf_pos++] = '%';
                    count++;
                    break;
                }
                
                default: {
                    buf[buf_pos++] = '%';
                    buf[buf_pos++] = fmt[i];
                    count += 2;
                    break;
                }
            }
        }
    }
    
    buf[buf_pos] = '\0';
    va_end(ap);
    
    return count;
}
